package com.ruizhong.integratedmediawithoutc.util;

import com.ruizhong.integratedmediawithoutc.server.WebSocketServer;
import com.ruizhong.integratedmediawithoutc.service.TMaterialService;
import com.ruizhong.integratedmediawithoutc.service.impl.TMaterialServiceImpl;
import org.springframework.stereotype.Component;

import javax.websocket.EncodeException;
import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.*;

@Component
public class H265ToH264Converter {
    private static Process ffmpegProcess;
    private static long totalDuration = 0; // 视频总时长(毫秒)
    private static volatile boolean isRunning = false;
    private static String videoName;

    private static TMaterialService tMaterialService;


    public static synchronized void convertVideo(String inputPath, String outputPath, String tokenId, TMaterialServiceImpl tMaterialService) throws IOException {
//        检验源文件是否为h264编码
        Integer is = checkSourceIs264(inputPath);
        totalDuration = getVideoDuration(inputPath);
        String[] split = inputPath.split("/");
        videoName = split[split.length - 1];
        // 获取视频总时长
        H265ToH264Converter.tMaterialService = tMaterialService;
        System.out.println("视频总时长: " + formatTime(totalDuration));
        System.out.println("视频名称: " + videoName);
        if (is != 0) {
            WSResult<Map<String, Object>> wsResult = new WSResult<>();
            HashMap<String, Object> wsResultMap = new HashMap<>();
            System.out.println("直接复制粘贴");
            Path source = Paths.get(inputPath);
            Path dest = Paths.get(outputPath);
            NIOFileCopyWithProgress.ProgressCallback callback = new NIOFileCopyWithProgress.ProgressCallback() {
                private final DecimalFormat df = new DecimalFormat("0.00");

                @Override
                public void onProgress(double percent) throws EncodeException, IOException {
                    double progress = Math.floor(percent);
                    System.out.println(progress);
                    wsResultMap.put("rest", "");
                    wsResultMap.put("type", 0);
                    wsResultMap.put("progress", progress);
                    wsResultMap.put("file", videoName);
                    wsResult.setData(wsResultMap);
                    WebSocketServer.sendInfo(wsResult, tokenId);
                    System.out.println("\r复制进度: " + df.format(percent) + "%");
                }

                @Override
                public void onComplete() throws EncodeException, IOException {
                    System.out.println("\n文件复制完成!");
                    tMaterialService.saveNewMaterial(totalDuration, videoName,is);
                }

                @Override
                public void onError(String message) {
                    System.err.println("\n错误: " + message);
                }
            };
            NIOFileCopyWithProgress.copyFile(source, dest, callback);
            return;
        }
        System.out.println("需要转码");
        try {
            // 构建FFmpeg命令
            String[] cmd = {
                    "ffmpeg",
                    "-i", inputPath,
                    "-c:v", "libx264",
                    "-preset", "veryslow",
                    "-crf", "18",
                    "-profile:v", "high",
                    "-level", "4.2",
                    "-pix_fmt", "yuv420p",
                    "-x264-params", "ref=6:bframes=8:aq-mode=3",
                    "-movflags", "+faststart",
                    "-c:a", "copy",
                    outputPath
            };


            // 启动FFmpeg进程
            ProcessBuilder pb = new ProcessBuilder(cmd);
            pb.redirectErrorStream(true); // 合并标准错误和标准输出
            ffmpegProcess = pb.start();

            isRunning = true;

            // 启动线程读取进度
            new Thread(new Runnable() {
                @Override
                public void run() {
                    readProcessOutput(tokenId);
                }
            }).start();

            int exitCode = ffmpegProcess.waitFor();
            isRunning = false;

            if (exitCode == 0) {
                System.out.println("\n转换完成!");
                tMaterialService.saveNewMaterial(totalDuration, videoName,1);
            } else {
                System.err.println("\n转换失败，退出码: " + exitCode);
            }

        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
            isRunning = false;
        }
    }

    private static Integer checkSourceIs264(String filePath) {
        String lowerPath = filePath.toLowerCase();
        if (lowerPath.endsWith(".pptx") || lowerPath.endsWith(".ppt")) {
            return 3; // 标识为PPT文件
        }
        try {
            Process process = new ProcessBuilder("ffmpeg", "-i", filePath)
                    .redirectErrorStream(true)
                    .start();

            BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()));

            String line;
            Pattern h264Pattern = Pattern.compile("Video:.*h264", Pattern.CASE_INSENSITIVE);
            Pattern imagePattern = Pattern.compile("Input #0, (png|jpeg|jpg|bmp|gif)", Pattern.CASE_INSENSITIVE);

            while ((line = reader.readLine()) != null) {
                if (h264Pattern.matcher(line).find()) {
                    process.destroy();
                    return 1;
                }else if( imagePattern.matcher(line).find()){
                    process.destroy();
                    return 2;
                }

            }

            process.waitFor();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 读取FFmpeg输出并解析进度
     *
     * @param tokenId
     */
    private static void readProcessOutput(String tokenId) {
        WSResult<Map<String, Object>> wsResult = new WSResult<>();
        HashMap<String, Object> wsResultMap = new HashMap<>();
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(ffmpegProcess.getInputStream()))) {

            String line;
            Pattern timePattern = Pattern.compile("time=(\\d{2}):(\\d{2}):(\\d{2})\\.(\\d{2})");

            while (isRunning && (line = reader.readLine()) != null) {
                // 打印FFmpeg输出（可选）
//                System.out.println(line);

                // 解析进度时间
                Matcher matcher = timePattern.matcher(line);
                if (matcher.find()) {
                    int hours = Integer.parseInt(matcher.group(1));
                    int minutes = Integer.parseInt(matcher.group(2));
                    int seconds = Integer.parseInt(matcher.group(3));
                    int ms = Integer.parseInt(matcher.group(4)) * 10;

                    long currentTime = hours * 3600000L +
                            minutes * 60000L +
                            seconds * 1000L +
                            ms;

                    if (totalDuration > 0) {
                        double progress = (double) currentTime / totalDuration * 100;
                        System.out.println(Math.floor(progress));
                        wsResultMap.put("rest", "");
                        wsResultMap.put("type", 0);
                        wsResultMap.put("progress", Math.floor(progress));
                        wsResultMap.put("file", videoName);
                        wsResult.setData(wsResultMap);
                        WebSocketServer.sendInfo(wsResult, tokenId);
                        System.out.printf("\r\n转换进度: %.2f%%", progress);
                    }
                }
            }
        } catch (IOException e) {
            if (isRunning) {
                e.printStackTrace();
            }
        } catch (EncodeException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取视频总时长
     */
    private static long getVideoDuration(String inputPath) throws IOException {
        String[] cmd = {"ffmpeg", "-i", inputPath};
        Process process = new ProcessBuilder(cmd).start();

        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(process.getErrorStream()))) {

            String line;
            Pattern durationPattern = Pattern.compile("Duration: (\\d{2}):(\\d{2}):(\\d{2})\\.(\\d{2})");

            while ((line = reader.readLine()) != null) {
                Matcher matcher = durationPattern.matcher(line);
                if (matcher.find()) {
                    int hours = Integer.parseInt(matcher.group(1));
                    int minutes = Integer.parseInt(matcher.group(2));
                    int seconds = Integer.parseInt(matcher.group(3));
                    int ms = Integer.parseInt(matcher.group(4)) * 10;

                    return hours * 3600000L +
                            minutes * 60000L +
                            seconds * 1000L +
                            ms;
                }
            }
        }
        return 0;
    }

    /**
     * 格式化时间显示
     */
    private static String formatTime(long milliseconds) {
        long seconds = milliseconds / 1000;
        return String.format("%02d:%02d:%02d",
                seconds / 3600, (seconds % 3600) / 60, seconds % 60);
    }

    /**
     * 停止转换
     */
    public void stopConversion() {
        if (ffmpegProcess != null && ffmpegProcess.isAlive()) {
            ffmpegProcess.destroy();
            isRunning = false;
        }
    }
}